using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace _3D_Game
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        // Model stuff
        ModelManager modelManager;

        // Camera
        public Camera camera { get; protected set; }

        // Random
        public Random rnd { get; protected set; }

        // Shot variables
        float shotSpeed = 10;
        int shotDelay = 300;
        int shotCountdown = 0;

        Texture2D blood;
        Vector2 bloodpos;
        public int life;
        string Directions = "Controls:\n Arrow Keys: Move \n Spacebar: Fire \n Esc: Exit";

        int px = 0;
        int py = 0;


        int timer = 0;
        int timeSinceLastFire = 0;
        bool powerup = false;
        //bool poweruphealth = false;
        // Audio
        AudioEngine audioEngine;
        WaveBank waveBank;
        SoundBank soundBank;
        Cue trackCue;

        // Game state items
        public enum GameState { START, PLAY, LEVEL_CHANGE, WIN, END }
        GameState currentGameState = GameState.START;

        // Splash screen and scoring
        SplashScreen splashScreen;
        int score = 0;
        SpriteFont scoreFont;
        SpriteFont directionsFont;

        // Powerup stuff
        int originalShotDelay = 300;
        public enum PowerUps { RAPID_FIRE, POWER_UP_FIRE, POWER_UP_HEALTH }
        int shotDelayRapidFire = 100;
        //int rapidFireTime = 10000;
        int powerUpCountdown = 0;
        string powerUpText = "";
        int powerUpTextTimer = 0;
        SpriteFont powerUpFont;

        int powerUpFireTime = 10000;
        private float CAMERA_BOUNDS_MAX_X = 140.0f;
        private float CAMERA_BOUNDS_MIN_X = -150.0f;
        private float CAMERA_BOUNDS_MAX_Y = 90.0f;
        private float CAMERA_BOUNDS_MIN_Y = -100.0f;
        private float CAMERA_BOUNDS_MAX_Z = 10.0f;
        private float CAMERA_BOUNDS_MIN_Z = -10.0f;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            rnd = new Random();

            // Set preferred resolution
            graphics.PreferredBackBufferWidth = 1000;
            graphics.PreferredBackBufferHeight = 600;

        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // Initialize Camera
            camera = new Camera(this, new Vector3(0, 50, 250),
                Vector3.Zero, Vector3.Up);
            Components.Add(camera);

            // Initialize model manager
            modelManager = new ModelManager(this);
            Components.Add(modelManager);
            modelManager.Enabled = false;
            modelManager.Visible = false;

            // Splash screen component
            splashScreen = new SplashScreen(this);
            Components.Add(splashScreen);
            splashScreen.SetData("The Fifth Space / 3D version",
                currentGameState);
            bloodpos = new Vector2(0, 0);
            life = 200;
            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);
          
            blood = Content.Load<Texture2D>(@"Textures\blood");
            // Load sounds and play initial sounds
            audioEngine = new AudioEngine(@"Content\Audio\GameAudio.xgs");
            waveBank = new WaveBank(audioEngine, @"Content\Audio\Wave Bank.xwb");
            soundBank = new SoundBank(audioEngine, @"Content\Audio\Sound Bank.xsb");
            
            // Play the soundtrack
            trackCue = soundBank.GetCue("Tracks");
            trackCue.Play();

            // Load fonts
            scoreFont = Content.Load<SpriteFont>(@"Fonts\ScoreFont");
            powerUpFont = Content.Load<SpriteFont>(@"Fonts\PowerupFont");
            directionsFont = Content.Load<SpriteFont>(@"Fonts\directions");


           
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            Matrix current = modelManager.player.GetWorld();

            KeyboardState k = Keyboard.GetState();


            // Allows the game to exit
            if (k.IsKeyDown(Keys.Escape))
                this.Exit();

            // Only check for shots if you're in the play game state
            if (currentGameState == GameState.PLAY)
            {
                //try to let player move
                //start with X axis
                if (k.IsKeyDown(Keys.Right)) // x
                {

                    px = 2;
                  
                }

                if (k.IsKeyDown(Keys.Left)) // -x
                {
                    
                    px = -2;
                  
                }

                // Y Axis
                if (k.IsKeyDown(Keys.Up)) // y
                {
                    py = 2;
                   
                }

                if (k.IsKeyDown(Keys.Down)) // -y
                {
                    py = -2;
                   
                }

                if (k.IsKeyDown(Keys.Down) && k.IsKeyDown(Keys.Up))
                {
                    px = 0; py = 0;
                }

                if (k.IsKeyDown(Keys.Right) && k.IsKeyDown(Keys.Left))
                {
                    px = 0; py = 0;
                }

                modelManager.playerPosition += new Vector3(px, py, 0);
                px = 0; py = 0;
                modelManager.player.world = Matrix.CreateTranslation(modelManager.playerPosition);

                modelManager.playerPosition = PerformCameraCollisionDetection(modelManager.playerPosition);

                // See if the player has fired a shot
                FireShots(gameTime);
                
                //let enemy fire at player
                EnemyShot(gameTime);
            }

            // Update power-up timer
            UpdatePowerUp(gameTime);

            base.Update(gameTime);
        }
        private Vector3 PerformCameraCollisionDetection(Vector3 Position)
        {
            //Vector3 newPos = Position;

            if (Position.X > CAMERA_BOUNDS_MAX_X)
                Position.X = CAMERA_BOUNDS_MAX_X;

            if (Position.X < CAMERA_BOUNDS_MIN_X)
                Position.X = CAMERA_BOUNDS_MIN_X;

            if (Position.Y > CAMERA_BOUNDS_MAX_Y)
                Position.Y = CAMERA_BOUNDS_MAX_Y;

            if (Position.Y < CAMERA_BOUNDS_MIN_Y)
                Position.Y = CAMERA_BOUNDS_MIN_Y;

            if (Position.Z > CAMERA_BOUNDS_MAX_Z)
                Position.Z = CAMERA_BOUNDS_MAX_Z;

            if (Position.Z < CAMERA_BOUNDS_MIN_Z)
                Position.Z = CAMERA_BOUNDS_MIN_Z;

            return Position;
        }
        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);

            if (currentGameState == GameState.START)
            {
                spriteBatch.Begin();

                spriteBatch.DrawString(directionsFont,
                     Directions,
                     new Vector2(Window.ClientBounds.Width
                         - directionsFont.MeasureString(
                             Directions).X - 10,
                         Window.ClientBounds.Height / 2 + 100),
                         Color.Bisque);

                spriteBatch.End();


            }
            base.Draw(gameTime);

            // Only draw crosshair if in play game state
            if (currentGameState == GameState.PLAY)
            {
              
                spriteBatch.Begin();
               
                // Draw the current score
                spriteBatch.Draw(blood, bloodpos, new Rectangle(0, 0, life, 18), Color.White);
                string scoreText = "Score: " + score;
                spriteBatch.DrawString(scoreFont, scoreText,
                    new Vector2(10, 15), Color.Red);

                // Let the player know how many enemies he must kill
                spriteBatch.DrawString(scoreFont, "Enemies Killed: " +
                    modelManager.enemyKilled + "/" + modelManager.levelInfoList[modelManager.currentLevel].missesAllowed,
                    new Vector2(10, scoreFont.MeasureString(scoreText).Y + 20),
                    Color.Red);

                // If power-up text timer is live, draw power-up text
                if (powerUpTextTimer > 0)
                {
                    powerUpTextTimer -= gameTime.ElapsedGameTime.Milliseconds;
                    Vector2 textSize = powerUpFont.MeasureString(powerUpText);
                    spriteBatch.DrawString(powerUpFont,
                        powerUpText,
                        new Vector2((Window.ClientBounds.Width / 2) -
                        (textSize.X / 2),
                        (Window.ClientBounds.Height / 2) -
                        (textSize.Y / 2)),
                        Color.Goldenrod);
                }

                spriteBatch.End();
            }
        }

        protected void EnemyShot(GameTime gameTime)
        {
            timer += gameTime.ElapsedGameTime.Milliseconds;
            float threshold = 500f;
            int fire = (int)MathHelper.Distance((int)timer, (int)timeSinceLastFire);
            
            if (fire > threshold)
            {
                modelManager.EnemyShot();
                timeSinceLastFire = timer;
            }
       
        }//enemy shot function

        protected void FireShots(GameTime gameTime)
        {
            if (shotCountdown <= 0)
            {
                // Did player press space bar?
                if (Keyboard.GetState().IsKeyDown(Keys.Space))
                {
                    Vector3 shotP = modelManager.player.GetWorld().Translation;///////////////////////////////////
                    Vector3 shotP1 = new Vector3(shotP.X - 2, shotP.Y, shotP.Z);
                    Vector3 shotP2 = new Vector3(shotP.X + 2, shotP.Y, shotP.Z);
                    Vector3 shotD = new Vector3(0, 0, -1);
                    if (powerup == true)
                    {
                        modelManager.AddShot( (shotP1), shotD * shotSpeed );
                        
                        modelManager.AddShot( (shotP2), shotD * shotSpeed);
                    }
                    else
                    {
                        modelManager.AddShot( (shotP), shotD * shotSpeed);
                    }
                    // Play shot audio
                    PlayCue("Shot");

                    // Reset the shot countdown
                    shotCountdown = shotDelay;
                }
            }
            else
                shotCountdown -= gameTime.ElapsedGameTime.Milliseconds;
        }

        public void PlayCue(string cue)
        {
            soundBank.PlayCue(cue);
        }

        public void ChangeGameState(GameState state, int level)
        {
            CancelPowerUps();

            currentGameState = state;

            switch (currentGameState)
            {
                case GameState.LEVEL_CHANGE:
                    splashScreen.SetData("Level " + (level + 1),
                        GameState.LEVEL_CHANGE);
                    modelManager.Enabled = false;
                    modelManager.Visible = false;
                    splashScreen.Enabled = true;
                    splashScreen.Visible = true;

                    // Stop the soundtrack loop
                    trackCue.Stop(AudioStopOptions.Immediate);
                    break;

                case GameState.PLAY:
                    modelManager.Enabled = true;
                    modelManager.Visible = true;
                    splashScreen.Enabled = false;
                    splashScreen.Visible = false;

                    if (trackCue.IsPlaying)
                        trackCue.Stop(AudioStopOptions.Immediate);

                    // To play a stopped cue, get the cue from the soundbank again
                    trackCue = soundBank.GetCue("Tracks");
                    trackCue.Play();
                    break;

                case GameState.END:
                    splashScreen.SetData("Game Over || Level : " + (level + 1) +
                        "|| Score : " + score, GameState.END);
                    modelManager.Enabled = false;
                    modelManager.Visible = false;
                    splashScreen.Enabled = true;
                    splashScreen.Visible = true;

                    // Stop the soundtrack loop
                    trackCue.Stop(AudioStopOptions.Immediate);
                    break;


                case GameState.WIN:
                    splashScreen.SetData("YOU WON! || Score : " + score, GameState.WIN);
                    modelManager.Enabled = false;
                    modelManager.Visible = false;
                    splashScreen.Enabled = true;
                    splashScreen.Visible = true;

                    // Stop the soundtrack loop
                    trackCue.Stop(AudioStopOptions.Immediate);
                    break;
            }
        }

        public void AddPoints(int points)
        {
            score += points;
        }

        public void ReduceBlood(int dlife)
        {
            life -= dlife;
        }

        private void CancelPowerUps()
        {
            shotDelay = originalShotDelay;
        }

        protected void UpdatePowerUp(GameTime gameTime)
        {
            if (powerUpCountdown > 0)
            {
                powerUpCountdown -= gameTime.ElapsedGameTime.Milliseconds;
                if (powerUpCountdown <= 0)
                {
                    CancelPowerUps();
                    powerUpCountdown = 0;
                    powerup = false;
                }
            }
        }

        public void StartPowerUp(PowerUps powerUp)
        {
            switch (powerUp)
            {
              
                case PowerUps.POWER_UP_FIRE:
                    shotDelay = shotDelayRapidFire;
                    powerup = true;
                    powerUpCountdown = powerUpFireTime;
                    powerUpText = "Double Fire Mode!";
                    powerUpTextTimer = 1000;
                    soundBank.PlayCue("RapidFire");
                    break;
                case PowerUps.POWER_UP_HEALTH:
                    //shotDelay = shotDelayRapidFire;
                    //poweruphealth = true;
                    //powerUpCountdown = rapidFireTime;
                    powerUpText = "HP!";
                    powerUpTextTimer = 1000;
                   // soundBank.PlayCue("RapidFire");
                    break;
            }
        }
    }
}
